/**
 * Copyright (C) 2011 Andrew C. Love (DNC) <dnc.app.sup@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.dnc.cloak.framework.reflect;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

public class ReflectionMethod {
	
    private static final Logger log = Logger.getLogger(ReflectionMethod.class);    
    private String methodClass = null;
    private String methodName = null;
    private java.lang.reflect.Method method = null;
    private Class<?>[] parmsClassArray = null;
    private List<ReflectionMethodParameter> parms = new ArrayList<ReflectionMethodParameter>();
    private String instance = null;


    public ReflectionMethod(String methodClassArg, String methodNameArg, String instanceArg) {
    	
        methodClass = methodClassArg;
        methodName = methodNameArg;
        instance = instanceArg;        
    }
    
    public ReflectionMethod(String methodClassArg, String methodNameArg) {    	
        methodClass = methodClassArg;
        methodName = methodNameArg;
        instance = "";        
    }    
        
    public ReflectionMethod(String methodClassArg, String propertyName, boolean getMethod) {    	
        methodClass = methodClassArg;
        methodName = (getMethod) ? "get" + propertyName.substring(0,1).toUpperCase() + propertyName.substring(1) : propertyName;
        instance = ""; 
        log.info(methodClass + "." + methodName);
    }    
    
    public void addParameter(ReflectionMethodParameter parameter) {
        parms.add(parameter);
    }
    
    public Object invokeWithArgs(Object target, Object[] args) throws ReflectionException {
        log.debug("invokeWithArgs:" + toString());        
        
        try {
            if (parmsClassArray==null) {
                parmsClassArray = new Class[parms.size()];
                for (int i = 0; i < parms.size(); i++) {
                    parmsClassArray[i] = ((ReflectionMethodParameter)parms.get(i)).getParameterClass();
                }
                Class<?> classForMethod = Class.forName(methodClass);
                
                method = classForMethod.getMethod(methodName,parmsClassArray);            
            } 
            
            Object rtn = method.invoke(target,args);

            log.debug("Return:" +  rtn );
            
            return rtn;
        }
        catch (Exception ex) {
            String message = "\nmethod=" + methodClass + "." + methodName +"\n";
            message+="arguments"; 
            //Add parm classes
            if (parmsClassArray!=null){
                for (int i = 0; i < parmsClassArray.length; i++) {
                    message +="\n\targ type " + i + "=" + "[" + parmsClassArray[i] + "]";
                }
            }            
            //Add parm values
            for (int i = 0; args!=null && i < args.length ; i++) {
                message+= "\n\targ value " + i + "=" + "[" + args[i]+ "]";                
            }            
            
            throw new ReflectionException(message,ex);
        }
    }

    public Object invoke (Object target, Map<String, Object> storage) throws ReflectionException {
    	
        log.debug("invoke:" + toString());        
        
        Object[] args = null;
        try {
            if (parmsClassArray==null) {
                parmsClassArray = new Class[parms.size()];
                for (int i = 0; i < parms.size(); i++) {
                    parmsClassArray[i] = ((ReflectionMethodParameter)parms.get(i)).getParameterClass();
                }
                Class<?> classForMethod = Class.forName(methodClass);
                method = classForMethod.getMethod(methodName,parmsClassArray);            
            } 
            
            args = new Object[parms.size()];
            for (int i = 0; i < args.length; i++) {
                args[i] = ((ReflectionMethodParameter)parms.get(i)).getParameterValue(storage);
                if (log.isDebugEnabled()){
                    log.debug("arg[" + i + "]=" + args[i]); 
                }
            }
            Object rtn = method.invoke(target,args);
            log.debug("Return:" +  rtn );
            
            return rtn;
        }
        catch (Exception ex) {
            String message = "\nmethod=" + methodClass + "." + methodName +"\n";
            message+="arguments"; 
            //Add parm classes
            if (parmsClassArray!=null){
                for (int i = 0; i < parmsClassArray.length; i++) {
                    message +="\n\targ type " + i + "=" + "[" + parmsClassArray[i] + "]";
                }
            }            
            //Add parm values
            for (int i = 0; i < parms.size(); i++) {
                message+= "\n\targ value " + i + "=" + "[" + ((ReflectionMethodParameter)parms.get(i)).getParameterValue(storage)+ "]";                
            }            
            
            throw new ReflectionException(message,ex);
        }
        
    }
    
    public String getInstanceType() {
        return instance;
    }

    public String getMethodClassName() {
        return methodClass;
    }

    public String getMethodName() {
        return methodName;
    }

    public  List<ReflectionMethodParameter> getParms() {
        return parms;
    }

    public Class<?>[] getParmsClassArray() {
        return parmsClassArray;
    }

    public String toString() {
    	
        StringBuffer sb = new StringBuffer();
        sb.append("->");
        sb.append(this.getMethodClassName());
        sb.append(".");
        sb.append(this.getMethodName());
        
        return sb.toString();
        
    }  
    
}